package io.jsen.meta.diff.watch;

import io.jsen.meta.diff.obj.MetaObject;
import io.jsen.meta.diff.obj.complex.MetaCollection;
import io.jsen.meta.diff.obj.complex.MetaComplex;
import io.jsen.meta.diff.obj.complex.MetaMap;
import io.jsen.meta.diff.obj.special.MetaNull;
import io.jsen.meta.diff.support.MetaModifier;

import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;

/**
 * @author jsen
 * @date 2021/04/30
 **/
public class MetaPathSearch {
    public static MetaObject<?, ?> search(MetaObject<?, ?> metaObject, MetaPath metaPath) {
        if (metaPath.fromRoot) {
            metaObject = getRoot(metaObject);
        }
        return innerSearch(metaObject, metaPath.iterator());
    }

    private static MetaObject<?, ?> innerSearch(MetaObject<?, ?> metaObject, Iterator<String> iterator) {
        if (iterator.hasNext()) {
            String key = iterator.next();
            if (!eq(key, metaObject.getMetaRelation().getMetaField().getKey())) {
                return new MetaNull(metaObject.getMetaRelation().getMetaField());
            }
            // eq && no next
            if (!iterator.hasNext()) {
                return metaObject;
            }
            int modifier = metaObject.getModifier();
            // eq && has next
            if (!MetaModifier.anyComplex(modifier)) {
                return new MetaNull(metaObject.getMetaRelation().getMetaField());
            } else if (MetaModifier.isComplex(modifier)) {
                MetaComplex complex = (MetaComplex) metaObject;
                return searchCollection(complex, complex.getVal().values(), iterator);
            } else if (MetaModifier.isMap(modifier)) {
                MetaMap<?> map = (MetaMap<?>) metaObject;
                return searchCollection(map, map.getVal().values(), iterator);
            } else if (MetaModifier.isCollection(modifier)) {
                MetaCollection collection = (MetaCollection) metaObject;
                return searchCollection(collection, collection.getVal(), iterator);
            }
        }
        return metaObject;
    }

    private static MetaObject<?, ?> searchCollection(MetaObject<?, ?> metaObject,
                                                     Collection<MetaObject<?, ?>> collection,
                                                     Iterator<String> iterator) {
        final String key = iterator.next();
        Optional<MetaObject<?, ?>> optional = collection.stream()
                .filter(obj -> eq(key, obj.getMetaRelation().getMetaField().getKey()))
                .findFirst();
        if (optional.isPresent()) {
            return innerSearch(optional.get(), iterator);
        }
        return new MetaNull(metaObject.getMetaRelation().getMetaField());
    }

    private static MetaObject<?, ?> getRoot(MetaObject<?, ?> metaObject) {
        if (metaObject.getMetaRelation().getParent() != null) {
            return getRoot(metaObject.getMetaRelation().getParent());
        }
        return metaObject;
    }

    private static boolean eq(String key, Object keyObj) {
        return (key == keyObj) || (keyObj != null && keyObj.toString().equals(key));
    }

    private MetaPathSearch() {
    }
}
